#pragma once
#include "vulkan_include.hpp"
#include "GLFW/glfw3.h"
#include "njm_window.hpp"
#include <tuple>
#include <vector>

namespace njm {

    class NjmDevice
    {
    private:
        VkDevice myVkDevice;
        VkInstance mInstance;
        VkPhysicalDevice mPhysicalDevice;
        VkPhysicalDeviceProperties mPhysicalDeviceProperties;
        VkQueueFamilyProperties mQueueFamilyProperties;
      
        uint32_t mGraphicsQueueFamilyIndex;
        VkSurfaceKHR mSurface;

        VkInstance createInstance(VkApplicationInfo appInfo);
        std::tuple<VkPhysicalDevice, VkDeviceQueueCreateInfo> selectPhysicalDevice(VkInstance instance);
        VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice, VkDeviceQueueCreateInfo queueCreateInfo);

        uint32_t findMemoryType(uint32_t requirementsMemoryTypeBits, VkMemoryPropertyFlags properties);
        uint32_t findQueueFamily(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface);
        void checkInstanceExtensions(const std::vector<const char*>& names);
        void checkInstanceLayers(const std::vector<const char*>& names);
        void checkDeviceExtensions(VkPhysicalDevice physicalDevice, const std::vector<const char*>& names);

    public:
        NjmDevice(NjmWindow& window);
        ~NjmDevice();
        NjmDevice(const NjmDevice &) = delete;
        NjmDevice& operator=(const NjmDevice &) = delete;

        VkDevice device();
        VkInstance getInstance() const { return mInstance; }
        uint32_t getGraphicsQueueFamilyIndex() const { return mGraphicsQueueFamilyIndex; }
        VkSurfaceKHR getWindowSurface() const { return mSurface; }
        VkPhysicalDevice getPhysicalDevice() const { return mPhysicalDevice; }
        VkPhysicalDeviceProperties getPhysicalDeviceProperties() const { return mPhysicalDeviceProperties; }
        VkQueueFamilyProperties getQueueFamilyProperties() const { return mQueueFamilyProperties; }
        VkCommandPool getCommandPool();

        void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer &buffer, VkDeviceMemory &bufferMemory);
        void createImageWithInfo(VkImageCreateInfo imgInfo, VkMemoryPropertyFlagBits memoryPropertyFlag, VkImage &image, VkDeviceMemory &imgMemory);
        void copyBuffer(VkBuffer srcBuf, VkBuffer dstBuf, VkDeviceSize bufSize);
        void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height);
    
    };

}